/// <reference types="@manycore/idp-sdk"/>
import { MsgAction, MSG_PREFIX } from "@/vm/constant";
import type { Msg } from "@/vm/types";
import { cloneDeep, uniqueId } from "lodash-es";

export { MsgAction as HandlerName };

export function getHandler<A extends Msg.Action>(handlerName: A) {
  type H = Msg.HandlerMap[A]
  type P = Parameters<H>
  type R = ReturnType<H>
  type V = Awaited<R>

  return (...params: P) => new Promise<V>((res, rej) => {
    const post = { action: handlerName, value: params }
    logMsg(post, MsgType.Input)
    addMsgHandler(post, (promise) => {
      promise
        .then((msgData) => {
          logMsg({ action: post.action, value: msgData.value }, MsgType.OutPut)
          return msgData
        })
        .then(msgData => {
          return msgData.value
        })
        .then(res)
        .catch(rej)
    })
  })
}

type BaseMsgData = { action: Msg.Action, value?: any }
type MsgData = BaseMsgData & { id: string }

const msgHandlerMap = new Map<MsgData, (promise: Promise<Msg.Data>) => void>()
window.addEventListener('message', ({ data }: MessageEvent<Msg.Data>) => {
  const { action, status, id } = data
  for (const post of msgHandlerMap.keys()) {
    if (action === MSG_PREFIX + post.action && id === id) {
      const cb = msgHandlerMap.get(post)
      if (!cb) return
      if (status) {
        cb(Promise.resolve(data))
      } else {
        cb(Promise.reject(data))
      }
      msgHandlerMap.delete(post)
    }
  }
})
function postMsg(msgData: MsgData) {
  window.parent.postMessage({ ...msgData, action: MSG_PREFIX + msgData.action }, '*')
}
function addMsgHandler(
  baseMsgData: BaseMsgData,
  msgHandler: (promise: Promise<Msg.Data>) => void
) {
  const id = uniqueId()
  const msgData = { ...baseMsgData, id }
  postMsg(msgData)
  msgHandlerMap.set(msgData, msgHandler)
}

const log = console.log.bind(console)
enum MsgType {
  Input,
  OutPut
}
const msgTypeMap = {
  [MsgType.Input]: '入参',
  [MsgType.OutPut]: '出参',
}
function logMsg(msgData: BaseMsgData, msgType: MsgType) {
  const { action, value } = msgData
  log('[IDP Msg]', action, `${msgTypeMap[msgType]}:`, cloneDeep(value));
}